interface Animal {
    live(): void;
}

interface Dog extends Animal {
    woof(): void;
}

// type Example1 = number
type Example1 = Dog extends Animal ? number : string;
// type Example1 = string
type Example2 = RegExp extends Animal ? number : string;

// conditional ? trueExpression : falseExpression;

// SomeType extends OtherType ? TrueType : FalseType;

interface IdLabel {
    id: number;
}

interface NameLabel {
    label: string;
}


function createLabel(id: number): IdLabel
function createLabel(name: string): NameLabel
function createLabel(nameOrId: string | number): IdLabel | NameLabel
function createLabel(nameOrId: string | number): IdLabel | NameLabel {
    throw 'unimplemented';
}

// encode that logic in a conditional type
type NameOrId<T extends number | string> = T extends number
    ? IdLabel
    : NameLabel;

function createLabel2<T extends number | string>(idOrName: T): NameOrId<T> {
    throw 'unimplemented';
}

// function createLabel2<"ts">(idOrName: "ts"): NameLabel
createLabel2('ts');
// function createLabel2<2.8>(idOrName: 2.8): IdLabel
createLabel2(2.8);
// function createLabel2<"ts" | 2.8>(idOrName: "ts" | 2.8): IdLabel | NameLabel
createLabel2(Math.random() > 0.5 ? 'ts' : 2.8);


type MessageOf<T> = T extends { message: unknown } ?  T['message'] : never;

interface Email {
    message: string;
}

// type EmailMessageContents = string
type EmailMessageContents = MessageOf<Email>;
// type Foo = never
type Foo = MessageOf<'str'>;

type Flatten<T> = T extends any[] ? T[number] : T;
// type Str = string
type Str = Flatten<string[]>;
// type Num = number
type Num = Flatten<number>

type Flatten2<T> = T extends Array<infer Item> ? Item : T;
// type Str = string
type Str2 = Flatten2<string[]>;
// type Num = number
type Num2 = Flatten2<number>

type GetReturnType<T> = T extends (...args: any[]) => infer Return
    ? Return
    : never

// type num = number
type num =  GetReturnType<() => number>;
// type num = string
type str =  GetReturnType<() => string>;
// type bool = boolean[] 
type bool =  GetReturnType<() => boolean[]>;


type ToArray<T> = T extends any ? T[] : never;
// type StringArrOrNumArr = string[] | number[]
type StringArrOrNumArr = ToArray<string | number>;

type ToArray2<T> = [T] extends [any] ? T[] : never;
// type StringOrNumArr = (string | number)[]
type StringOrNumArr = ToArray2<string | number>;